﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using J_FoundationClasses.Programacao;

/*
 * A responsabilidade de um diretor e o gerenciamento de diretivas.
 * Um Montador vai criar exatemente um diretor.
 * O Diretor faz parte do assembly sintaxe em vez de assembly Montador pq. ele define
 * a sintaxe de diretivas da linguagem.
 * 
 */

namespace J_FoundationClasses.Programacao
{
    public class Diretor
    {
        // Variaveis que guardam os resultados de diretivas com efeito
        // ao programa inteiro
        //
        public string ProgName;
        public string ProgStart;

        // Colecão de diretivas
        //
        public static Tokens Diretivas;

        // Constructor
        //
        public Diretor()
        {
            Diretivas = IniDiretivas();
        }

        Tokens IniDiretivas()
        {
            Tokens tt = new Tokens();

            tt.Itens.Add(new Diretiva(".org", MH_null, MV_org, "{0} {1} {2} {3}"));
            tt.Itens.Add(new Diretiva(".prog", MH_prog, MV_prog, "{0} {1} {2} {3}"));
            tt.Itens.Add(new Diretiva(".start", MH_start, MV_start, "{0} {1} {2} {3}"));
            tt.Itens.Add(new Diretiva(".absoluto", MH_abs, MV_start, "{0} {1} {2} {3}"));
            tt.Itens.Add(new Diretiva(".compativel", MH_compat, MV_start, "{0} {1} {2} {3}"));

            // mais .byte, .word, .dw, .string, .space, .align
            return tt;
        }




        // Métodos que atribuem valores aos endereços da instrução/próxima
        //
        //

        UInt64 MV_org(UInt64 Inicio, ref Instrucao Inst)
        {
            // Diretiva .org vai setar o valor especificado como endereço próximo

            UInt64 uu;
            Valor vv = new Valor(Inst.Imediato);

            if (vv.Tipo == TipoVal.Vazio) throw new ArgumentException(".org precisa um argumento.");
            if (vv.Tipo == TipoVal.Ilegal) throw new ArgumentException(vv.Mensagem);
            if (vv.Tipo == TipoVal.Ident) throw new ArgumentException(".org não aceita parâmetro simbólico.");
            if (vv.Tipo != TipoVal.NumInt) throw new ArgumentException(".org exige um parâmetro numérico.");
            if (vv.ValNum < 0) throw new ArgumentException(".org não aceita valor negativo.");
            uu = (UInt64)vv.ValNum;
            Inst.edcAtual = uu;
            Inst.edcProx = uu;
            return uu;
        }
        UInt64 MV_prog(UInt64 Inicio, ref Instrucao Inst)
        {
            // Diretiva .program vai setar o nome do programa executavel
            // Não manipula endereços
            Inst.edcAtual = Inicio;
            Inst.edcProx = Inicio;
            return Inicio;
        }
        UInt64 MV_start(UInt64 Inicio, ref Instrucao Inst)
        {
            // Diretiva .start vai setar o endereço da primeira instrução a executar
            // Não manipula endereços
            Inst.edcAtual = Inicio;
            Inst.edcProx = Inicio;
            return Inicio;
        }

        // Métodos que retornam imagem binária ou produzem símbolos, proriedades
        //
        //

        void MH_null(ref Instrucao inst)
        {
            // esta instrução não produz imagem binária
            inst.RepBin = null;
        }
        void MH_prog(ref Instrucao inst)
        {
            MH_null(ref inst);
            Valor vv = new Valor(inst.Imediato);
            if (vv.Tipo == TipoVal.Vazio) throw new ApplicationException("Diretiva .prog exige parâmetro.");
            if (vv.Tipo == TipoVal.Ilegal) throw new ApplicationException(vv.Mensagem);
            if (vv.Tipo != TipoVal.Ident) throw new ApplicationException("Diretiva .prog exige um identificador.");

            SimboloFonte ss = new SimboloFonte(TipoSimbo.Programa, vv.Lexema);
            inst.DefSim = ss;
        }
        void MH_abs(ref Instrucao inst)
        {
            // constatar que o programa utiliza endereços absolutos e não aceita relocação
            MH_null(ref inst);

            PropFonte pp = new PropFonte(TipoProp.Absoluto, (object)true);
            inst.Prop = pp;
        }
        void MH_start(ref Instrucao inst)
        {
            MH_null(ref inst);
            Valor vv = new Valor(inst.Imediato);
            if (vv.Tipo == TipoVal.Vazio) throw new ApplicationException("Diretiva .start exige parâmetro.");
            if (vv.Tipo == TipoVal.Ilegal) throw new ApplicationException(vv.Mensagem);
            if (vv.Tipo != TipoVal.Ident) throw new ApplicationException("Diretiva .start exige um identificador.");

            SimboloFonte ss = new SimboloFonte(TipoSimbo.Rotulo, vv.Lexema);
            ss.Start = true;
            inst.DefSim = ss;
        }
        void MH_compat(ref Instrucao inst)
        {
            // gravar identidade de processador compativel como array de 8 bytes
            MH_null(ref inst);
            Valor vv = new Valor(inst.Imediato);
            if (vv.Tipo == TipoVal.Vazio) throw new ApplicationException("Diretiva .compativel exige parâmetro.");
            if (vv.Tipo == TipoVal.Ilegal) throw new ApplicationException(vv.Mensagem);
            if (vv.Tipo != TipoVal.Ident) throw new ApplicationException("Diretiva .compativel exige um identificador.");
            if (vv.Lexema.Length == 0) throw new ApplicationException("Diretiva .compativel exige um identificador de tamanho > 0.");
            if (vv.Lexema.Length > 8) throw new ApplicationException("Diretiva .compativel exige um identificador de tamanho <= 8.");

            byte[] bb = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
            for (int ii = 0; ii < vv.Lexema.Length; ii++) bb[ii] = (byte)vv.Lexema[ii];
            PropFonte pp = new PropFonte(TipoProp.Absoluto, (object)bb);
            inst.Prop = pp;
        }

    }
}
